package com.litong.modules.face.recognize.utils;

import java.util.Arrays;

import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import lombok.extern.slf4j.Slf4j;

/**
 * @author bill robot
 * @date 2020年9月6日_下午4:23:26 
 * @version 1.0 
 * @desc 人脸工具类包含人脸检测和人脸比对
 */
@Slf4j
public class FaceUtils {

  /**
   * opencv实现人脸识别，同时检测到人脸和人眼时才截图
   */
  public static Mat detectFace(Mat mat, CascadeClassifier faceDetector, CascadeClassifier eyeDetector) {

    // 检测图片中人脸,将结果保存到faceDetections
    MatOfRect faceDetections = new MatOfRect();
    faceDetector.detectMultiScale(mat, faceDetections);

    // 取出结果
    Rect[] rects = faceDetections.toArray();
    if (rects != null && rects.length >= 1) {
      drawRectAndDetectEyes(mat, rects, eyeDetector);
    } else {
      // log.info("没有检测到人脸");
    }
    return mat;
  }

  /**
   * 添加矩形并且检测眼睛
   * @param mat
   * @param eyeDetector
   * @param rects
   */
  public static void drawRectAndDetectEyes(Mat mat, Rect[] rects, CascadeClassifier eyeDetector) {
    for (Rect rect : rects) {
      // 画矩形
      Scalar red = new Scalar(0, 0, 255);
      MatImageUtils.drawRect(mat, rect, red);

      // 取出矩形的区域
      Mat faceROI = new Mat(mat, rect);
      // 识别人眼
      MatOfRect eyesDetections = new MatOfRect();
      eyeDetector.detectMultiScale(faceROI, eyesDetections);
      // 获取识别结果
      Rect[] eyesRect = eyesDetections.toArray();
      if (eyesRect == null || eyesRect.length == 0) {
        return;
      }
      log.info("检测眼睛数量:{}", eyesRect.length);
      // 画出矩形区域
      for (Rect r : eyesRect) {

        MatImageUtils.drawRect(faceROI, r, red);
      }

      // 保存图片
      MatImageUtils.saveMat(faceROI);
    }
  }

  /**
   * 比较两张人脸的图片,返回图片的相似度
   */
  public static double compareImage(String src, String dst, CascadeClassifier classifier) {
    Mat mat_1 = convertToMat(src,classifier);
    Mat mat_2 = convertToMat(dst,classifier);

    // 颜色范围
    MatOfFloat ranges = new MatOfFloat(0f, 256f);
    // 直方图大小， 越大匹配越精确 (越慢)
    MatOfInt histSize = new MatOfInt(1000);

    Mat hist_1 = new Mat();
    Mat hist_2 = new Mat();
    Imgproc.calcHist(Arrays.asList(mat_1), new MatOfInt(0), new Mat(), hist_1, histSize, ranges);
    Imgproc.calcHist(Arrays.asList(mat_2), new MatOfInt(0), new Mat(), hist_2, histSize, ranges);

    // CORREL 相关系数
    double res = Imgproc.compareHist(hist_1, hist_2, Imgproc.CV_COMP_CORREL);
    return res;
  }

  /**
   * 1.灰度化 2.取出图片中包含人脸的区域 并返回
   */
  public static Mat convertToMat(String imagePath,CascadeClassifier classifier) {
    Mat image0 = Imgcodecs.imread(imagePath);
    Mat image1 = new Mat();
    // 灰度化
    Imgproc.cvtColor(image0, image1, Imgproc.COLOR_BGR2GRAY);
    // 探测人脸
    MatOfRect faceDetections = new MatOfRect();
    classifier.detectMultiScale(image1, faceDetections);
    // 取出rect中人脸图片的范围,并返回
    for (Rect rect : faceDetections.toArray()) {
      Mat face = new Mat(image1, rect);
      return face;
    }
    return null;
  }
}
